<script>
import { mapState, mapActions } from 'pinia'
import { useUserStore } from '../stores/06.axios(选项式)'

export default {
  data() {
    return {
      // 新用户表单
      newUser: {
        first_name: '',
        last_name: '',
        email: '',
        avatar: ''
      },
      // 编辑用户表单
      editUser: {
        id: null,
        first_name: '',
        last_name: '',
        email: '',
        avatar: ''
      },
      // 是否显示编辑表单
      showEditForm: false,
      // 操作结果消息
      message: '',
      // 消息类型
      messageType: 'success',
      // 是否显示消息
      showMessage: false
    }
  },
  computed: {
    // 获取store实例
    userStore() {
      return useUserStore()
    },
    
    // 映射store中的状态
    ...mapState(useUserStore, [
      'users',
      'loading',
      'error',
      'currentUser',
      'pagination'
    ]),
    
    // 映射store中的getters
    ...mapState(useUserStore, [
      'totalUsers',
      'hasSelectedUser'
    ]),
    
    // 计算分页页数
    totalPages() {
      return Math.ceil(this.pagination.total / this.pagination.perPage)
    },
    
    // 计算分页数组
    pages() {
      const pages = []
      for (let i = 1; i <= this.totalPages; i++) {
        pages.push(i)
      }
      return pages
    }
  },
  methods: {
    // 映射store中的actions
    ...mapActions(useUserStore, [
      'fetchUsers',
      'fetchUserById',
      'setPage',
      'clearCurrentUser'
    ]),
    
    // 组件挂载后获取用户列表
    async loadUsers() {
      try {
        await this.fetchUsers()
      } catch (error) {
        this.showNotification('获取用户列表失败', 'error')
      }
    },
    
    // 查看用户详情
    async viewUserDetails(userId) {
      try {
        await this.fetchUserById(userId)
      } catch (error) {
        this.showNotification('获取用户详情失败', 'error')
      }
    },
    
    // 创建新用户
    async createUser() {
      if (!this.validateUserForm(this.newUser)) {
        this.showNotification('请填写完整的用户信息', 'error')
        return
      }
      
      try {
        await this.userStore.createUser(this.newUser)
        this.showNotification('用户创建成功', 'success')
        this.resetNewUserForm()
      } catch (error) {
        this.showNotification('创建用户失败', 'error')
      }
    },
    
    // 准备编辑用户
    prepareEditUser(user) {
      this.editUser = { ...user }
      this.showEditForm = true
    },
    
    // 更新用户
    async updateUser() {
      if (!this.validateUserForm(this.editUser)) {
        this.showNotification('请填写完整的用户信息', 'error')
        return
      }
      
      try {
        const userId = this.editUser.id
        const userData = {
          first_name: this.editUser.first_name,
          last_name: this.editUser.last_name,
          email: this.editUser.email,
          avatar: this.editUser.avatar
        }
        
        await this.userStore.updateUser(userId, userData)
        this.showNotification('用户更新成功', 'success')
        this.cancelEdit()
      } catch (error) {
        this.showNotification('更新用户失败', 'error')
      }
    },
    
    // 删除用户
    async deleteUser(userId) {
      if (!confirm('确定要删除此用户吗？')) {
        return
      }
      
      try {
        await this.userStore.deleteUser(userId)
        this.showNotification('用户删除成功', 'success')
      } catch (error) {
        this.showNotification('删除用户失败', 'error')
      }
    },
    
    // 取消编辑
    cancelEdit() {
      this.showEditForm = false
      this.editUser = {
        id: null,
        first_name: '',
        last_name: '',
        email: '',
        avatar: ''
      }
    },
    
    // 重置新用户表单
    resetNewUserForm() {
      this.newUser = {
        first_name: '',
        last_name: '',
        email: '',
        avatar: ''
      }
    },
    
    // 验证用户表单
    validateUserForm(user) {
      return user.first_name && user.last_name && user.email
    },
    
    // 显示通知消息
    showNotification(message, type = 'success') {
      this.message = message
      this.messageType = type
      this.showMessage = true
      
      // 3秒后自动隐藏消息
      setTimeout(() => {
        this.showMessage = false
      }, 3000)
    }
  },
  mounted() {
    // 组件挂载后加载用户列表
    this.loadUsers()
  },
  beforeUnmount() {
    // 组件卸载前重置store状态
    this.userStore.resetState()
  }
}
</script>

<template>
  <div class="tutorial-container">
    <header>
      <h1>06.axios(选项式)</h1>
      <p class="subtitle">学习如何在选项式API中结合Pinia和Axios进行API请求</p>
    </header>

    <main>
      <section class="intro">
        <h2>Axios封装与Pinia结合</h2>
        <p>在实际项目中，我们通常会对Axios进行封装，以便统一处理请求和响应，添加拦截器，处理错误等。本章将介绍如何封装Axios并与Pinia结合使用。</p>
        
        <div class="subsection">
          <h3>1. 基本封装 - request.js</h3>
          <p>首先，我们创建一个基础的axios实例，并添加拦截器：</p>
          <div class="code-example">
            <pre><code>// src/utils/request.js
import axios from 'axios'

// 创建axios实例
const service = axios.create({
  baseURL: 'https://api.example.com', // 基础URL
  timeout: 10000, // 请求超时时间
  headers: {
    'Content-Type': 'application/json'
  }
})

// 请求拦截器
service.interceptors.request.use(
  config => {
    // 在发送请求之前做些什么
    // 例如：添加token
    const token = localStorage.getItem('token')
    if (token) {
      config.headers['Authorization'] = `Bearer ${token}`
    }
    return config
  },
  error => {
    // 对请求错误做些什么
    return Promise.reject(error)
  }
)

// 响应拦截器
service.interceptors.response.use(
  response => {
    // 对响应数据做点什么
    return response.data
  },
  error => {
    // 对响应错误做点什么
    return Promise.reject(error)
  }
)

// 导出封装的请求方法
export function get(url, params) {
  return service({
    url,
    method: 'get',
    params
  })
}

export function post(url, data) {
  return service({
    url,
    method: 'post',
    data
  })
}

export default service</code></pre>
          </div>
        </div>
        
        <div class="subsection">
          <h3>2. API模块化 - api/user.js</h3>
          <p>然后，我们可以为不同的业务模块创建API文件，集中管理API请求：</p>
          <div class="code-example">
            <pre><code>// src/api/user.js
import { get, post, put, del } from '../utils/request'

// 用户API接口封装
export default {
  // 获取用户列表
  getUsers(params) {
    return get('/users', params)
  },
  
  // 获取单个用户
  getUser(id) {
    return get(`/users/${id}`)
  },
  
  // 创建用户
  createUser(data) {
    return post('/users', data)
  },
  
  // 更新用户
  updateUser(id, data) {
    return put(`/users/${id}`, data)
  },
  
  // 删除用户
  deleteUser(id) {
    return del(`/users/${id}`)
  }
}</code></pre>
          </div>
        </div>
        
        <div class="subsection">
          <h3>3. 与Pinia结合 - stores/user.js</h3>
          <p>最后，在Pinia store中使用封装的API：</p>
          <div class="code-example">
            <pre><code>// src/stores/user.js
import { defineStore } from 'pinia'
import userApi from '../api/user'

export const useUserStore = defineStore('users', {
  state: () => ({
    users: [],
    loading: false,
    error: null
  }),
  actions: {
    async fetchUsers(params) {
      this.loading = true
      this.error = null
      
      try {
        const response = await userApi.getUsers(params)
        this.users = response.data
        return response
      } catch (error) {
        this.error = error.message
        throw error
      } finally {
        this.loading = false
      }
    }
  }
})</code></pre>
          </div>
        </div>
      </section>

      <div class="demo-section">
        <!-- 通知消息 -->
        <div v-if="showMessage" :class="['notification', messageType]">
          {{ message }}
        </div>
        
        <div class="user-management">
          <h2>用户管理示例</h2>
          
          <div class="dashboard">
            <!-- 用户列表部分 -->
            <div class="card users-list">
              <h3>用户列表</h3>
              
              <div v-if="loading" class="loading-indicator">
                <div class="spinner"></div>
                <p>加载中...</p>
              </div>
              
              <div v-else-if="error" class="error-message">
                <p>{{ error }}</p>
                <button @click="loadUsers" class="btn primary">重试</button>
              </div>
              
              <div v-else>
                <div class="users-grid">
                  <div v-for="user in users" :key="user.id" class="user-card">
                    <div class="user-avatar">
                      <img :src="user.avatar" :alt="user.first_name" />
                    </div>
                    <div class="user-info">
                      <h4>{{ user.first_name }} {{ user.last_name }}</h4>
                      <p>{{ user.email }}</p>
                    </div>
                    <div class="user-actions">
                      <button @click="viewUserDetails(user.id)" class="btn small primary">查看</button>
                      <button @click="prepareEditUser(user)" class="btn small secondary">编辑</button>
                      <button @click="deleteUser(user.id)" class="btn small danger">删除</button>
                    </div>
                  </div>
                </div>
                
                <!-- 分页 -->
                <div class="pagination">
                  <button 
                    v-for="page in pages" 
                    :key="page" 
                    @click="setPage(page)" 
                    :class="['page-btn', { active: pagination.page === page }]"
                  >
                    {{ page }}
                  </button>
                </div>
              </div>
            </div>
            
            <!-- 用户详情部分 -->
            <div class="card user-details">
              <div v-if="!hasSelectedUser && !showEditForm">
                <h3>添加新用户</h3>
                <div class="user-form">
                  <div class="form-group">
                    <label>名字</label>
                    <input v-model="newUser.first_name" type="text" placeholder="输入名字" />
                  </div>
                  <div class="form-group">
                    <label>姓氏</label>
                    <input v-model="newUser.last_name" type="text" placeholder="输入姓氏" />
                  </div>
                  <div class="form-group">
                    <label>邮箱</label>
                    <input v-model="newUser.email" type="email" placeholder="输入邮箱" />
                  </div>
                  <div class="form-group">
                    <label>头像URL</label>
                    <input v-model="newUser.avatar" type="text" placeholder="输入头像URL" />
                  </div>
                  <button @click="createUser" class="btn primary" :disabled="loading">
                    {{ loading ? '提交中...' : '创建用户' }}
                  </button>
                </div>
              </div>
              
              <div v-else-if="showEditForm">
                <h3>编辑用户</h3>
                <div class="user-form">
                  <div class="form-group">
                    <label>名字</label>
                    <input v-model="editUser.first_name" type="text" placeholder="输入名字" />
                  </div>
                  <div class="form-group">
                    <label>姓氏</label>
                    <input v-model="editUser.last_name" type="text" placeholder="输入姓氏" />
                  </div>
                  <div class="form-group">
                    <label>邮箱</label>
                    <input v-model="editUser.email" type="email" placeholder="输入邮箱" />
                  </div>
                  <div class="form-group">
                    <label>头像URL</label>
                    <input v-model="editUser.avatar" type="text" placeholder="输入头像URL" />
                  </div>
                  <div class="form-actions">
                    <button @click="updateUser" class="btn primary" :disabled="loading">
                      {{ loading ? '更新中...' : '更新用户' }}
                    </button>
                    <button @click="cancelEdit" class="btn secondary">取消</button>
                  </div>
                </div>
              </div>
              
              <div v-else>
                <h3>用户详情</h3>
                <div v-if="loading" class="loading-indicator">
                  <div class="spinner"></div>
                  <p>加载中...</p>
                </div>
                
                <div v-else-if="currentUser" class="user-detail-view">
                  <div class="user-avatar large">
                    <img :src="currentUser.avatar" :alt="currentUser.first_name" />
                  </div>
                  <div class="user-detail-info">
                    <h4>{{ currentUser.first_name }} {{ currentUser.last_name }}</h4>
                    <p><strong>邮箱:</strong> {{ currentUser.email }}</p>
                    <p><strong>ID:</strong> {{ currentUser.id }}</p>
                  </div>
                  <div class="user-detail-actions">
                    <button @click="prepareEditUser(currentUser)" class="btn secondary">编辑</button>
                    <button @click="deleteUser(currentUser.id)" class="btn danger">删除</button>
                    <button @click="clearCurrentUser" class="btn primary">返回</button>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
      
      <section class="explanation">
        <h2>Axios封装的最佳实践</h2>
        
        <div class="subsection">
          <h3>1. 请求拦截器的使用场景</h3>
          <p>请求拦截器可以在发送请求前统一处理请求配置：</p>
          <ul>
            <li><strong>添加认证信息</strong>：自动为请求添加token</li>
            <li><strong>请求参数处理</strong>：转换或格式化请求参数</li>
            <li><strong>请求日志</strong>：记录请求信息，用于调试</li>
            <li><strong>请求取消</strong>：实现请求防抖或节流</li>
            <li><strong>加载状态管理</strong>：自动显示全局加载状态</li>
          </ul>
          <div class="code-example">
            <pre><code>// 请求拦截器示例
service.interceptors.request.use(
  config => {
    // 显示全局加载状态
    showLoading()
    
    // 添加token
    const token = localStorage.getItem('token')
    if (token) {
      config.headers['Authorization'] = `Bearer ${token}`
    }
    
    // 添加时间戳防止缓存
    if (config.method === 'get') {
      config.params = {
        ...config.params,
        _t: Date.now()
      }
    }
    
    return config
  },
  error => {
    hideLoading()
    return Promise.reject(error)
  }
)</code></pre>
          </div>
        </div>
        
        <div class="subsection">
          <h3>2. 响应拦截器的使用场景</h3>
          <p>响应拦截器可以统一处理服务器响应：</p>
          <ul>
            <li><strong>数据转换</strong>：统一处理响应数据格式</li>
            <li><strong>错误处理</strong>：统一处理HTTP错误和业务错误</li>
            <li><strong>权限验证</strong>：处理401、403等权限相关错误</li>
            <li><strong>刷新token</strong>：当token过期时自动刷新</li>
            <li><strong>响应日志</strong>：记录响应信息，用于调试</li>
          </ul>
          <div class="code-example">
            <pre><code>// 响应拦截器示例
service.interceptors.response.use(
  response => {
    // 隐藏全局加载状态
    hideLoading()
    
    const res = response.data
    
    // 假设服务器返回的数据格式为 { code, data, message }
    if (res.code !== 0) {
      // 处理业务错误
      showErrorMessage(res.message || '请求失败')
      
      // 处理特定错误码
      if (res.code === 401) {
        // token过期，重新登录
        logout()
        return Promise.reject(new Error('登录已过期，请重新登录'))
      }
      
      return Promise.reject(new Error(res.message || '未知错误'))
    }
    
    // 只返回数据部分
    return res.data
  },
  error => {
    hideLoading()
    
    // 处理HTTP错误
    if (error.response) {
      switch (error.response.status) {
        case 401:
          logout()
          showErrorMessage('未授权，请重新登录')
          break
        case 403:
          showErrorMessage('拒绝访问')
          break
        case 404:
          showErrorMessage('请求的资源不存在')
          break
        case 500:
          showErrorMessage('服务器内部错误')
          break
        default:
          showErrorMessage(`请求失败: ${error.response.status}`)
      }
    } else if (error.request) {
      showErrorMessage('服务器未响应')
    } else {
      showErrorMessage('请求配置错误')
    }
    
    return Promise.reject(error)
  }
)</code></pre>
          </div>
        </div>
        
        <div class="subsection">
          <h3>3. 请求方法封装</h3>
          <p>封装常用的请求方法，简化API调用：</p>
          <div class="code-example">
            <pre><code>// 封装GET请求
export function get(url, params, config = {}) {
  return service({
    url,
    method: 'get',
    params,
    ...config
  })
}

// 封装POST请求
export function post(url, data, config = {}) {
  return service({
    url,
    method: 'post',
    data,
    ...config
  })
}

// 封装上传文件的POST请求
export function uploadFile(url, file, config = {}) {
  const formData = new FormData()
  formData.append('file', file)
  
  return service({
    url,
    method: 'post',
    data: formData,
    headers: {
      'Content-Type': 'multipart/form-data'
    },
    ...config
  })
}

// 封装下载文件的GET请求
export function downloadFile(url, params, filename, config = {}) {
  return service({
    url,
    method: 'get',
    params,
    responseType: 'blob',
    ...config
  }).then(blob => {
    const link = document.createElement('a')
    const objectUrl = URL.createObjectURL(blob)
    link.href = objectUrl
    link.download = filename
    link.click()
    URL.revokeObjectURL(objectUrl)
  })
}</code></pre>
          </div>
        </div>
        
        <div class="subsection">
          <h3>4. 与Pinia结合的最佳实践</h3>
          <p>在Pinia中使用封装的API服务：</p>
          <ul>
            <li><strong>模块化API服务</strong>：为每个业务模块创建单独的API服务</li>
            <li><strong>在Store中统一管理请求状态</strong>：loading、error等状态</li>
            <li><strong>缓存策略</strong>：实现数据缓存，避免重复请求</li>
            <li><strong>乐观更新</strong>：在请求发送前先更新UI，提升用户体验</li>
            <li><strong>请求取消</strong>：组件卸载时取消未完成的请求</li>
          </ul>
          <div class="code-example">
            <pre><code>// 在Pinia store中使用API服务
import { defineStore } from 'pinia'
import userApi from '../api/user'

export const useUserStore = defineStore('users', {
  state: () => ({
    users: [],
    userCache: {}, // 用于缓存用户详情
    loading: false,
    error: null,
    // 用于取消请求
    abortController: null
  }),
  actions: {
    // 带缓存的获取用户详情
    async getUserById(id) {
      // 如果缓存中有数据，直接返回
      if (this.userCache[id]) {
        return this.userCache[id]
      }
      
      this.loading = true
      
      // 创建AbortController用于取消请求
      this.abortController = new AbortController()
      
      try {
        const user = await userApi.getUser(id, {
          signal: this.abortController.signal
        })
        
        // 缓存数据
        this.userCache[id] = user
        return user
      } catch (error) {
        if (error.name === 'AbortError') {
          console.log('请求被取消')
        } else {
          this.error = error.message
          throw error
        }
      } finally {
        this.loading = false
        this.abortController = null
      }
    },
    
    // 乐观更新示例
    async updateUser(id, userData) {
      // 保存原始数据，以便回滚
      const originalUser = { ...this.userCache[id] }
      
      // 乐观更新UI
      if (this.userCache[id]) {
        this.userCache[id] = { ...this.userCache[id], ...userData }
      }
      
      try {
        // 发送实际请求
        const updatedUser = await userApi.updateUser(id, userData)
        // 更新缓存
        this.userCache[id] = updatedUser
        return updatedUser
      } catch (error) {
        // 请求失败，回滚UI
        if (this.userCache[id]) {
          this.userCache[id] = originalUser
        }
        this.error = error.message
        throw error
      }
    },
    
    // 取消所有进行中的请求
    cancelRequests() {
      if (this.abortController) {
        this.abortController.abort()
        this.abortController = null
      }
    }
  },
  // 在组件卸载时自动取消请求
  onUnmounted() {
    this.cancelRequests()
  }
})</code></pre>
          </div>
        </div>
      </section>
    </main>
  </div>
</template>

<style scoped>
.tutorial-container {
  max-width: 1200px;
  margin: 0 auto;
  padding: 20px;
}

header {
  margin-bottom: 30px;
  border-bottom: 1px solid #eee;
  padding-bottom: 20px;
}

h1 {
  font-size: 2.5rem;
  margin-bottom: 10px;
}

.subtitle {
  font-size: 1.2rem;
  color: #666;
  margin-bottom: 20px;
}

.intro {
  margin-bottom: 40px;
}

.code-example {
  background-color: #f8f8f8;
  border-radius: 6px;
  padding: 15px;
  margin: 15px 0;
  overflow-x: auto;
}

pre {
  margin: 0;
}

code {
  font-family: 'Courier New', monospace;
  font-size: 0.9rem;
}

.demo-section {
  margin-bottom: 40px;
}

.notification {
  padding: 10px 15px;
  border-radius: 4px;
  margin-bottom: 20px;
}

.notification.success {
  background-color: #dff0d8;
  color: #3c763d;
  border: 1px solid #d6e9c6;
}

.notification.error {
  background-color: #f2dede;
  color: #a94442;
  border: 1px solid #ebccd1;
}

.dashboard {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 20px;
}

.card {
  background-color: white;
  border-radius: 8px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  padding: 20px;
  margin-bottom: 20px;
}

.users-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
  gap: 15px;
  margin-bottom: 20px;
}

.user-card {
  border: 1px solid #eee;
  border-radius: 6px;
  padding: 15px;
  display: flex;
  flex-direction: column;
}

.user-avatar {
  width: 80px;
  height: 80px;
  border-radius: 50%;
  overflow: hidden;
  margin: 0 auto 15px;
}

.user-avatar.large {
  width: 120px;
  height: 120px;
}

.user-avatar img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.user-info {
  text-align: center;
  margin-bottom: 15px;
}

.user-info h4 {
  margin-bottom: 5px;
}

.user-actions {
  display: flex;
  justify-content: center;
  gap: 10px;
  margin-top: auto;
}

.user-form {
  margin-top: 20px;
}

.form-group {
  margin-bottom: 15px;
}

label {
  display: block;
  margin-bottom: 5px;
  font-weight: 500;
}

input {
  width: 100%;
  padding: 8px 12px;
  border: 1px solid #ddd;
  border-radius: 4px;
  font-size: 1rem;
}

.form-actions {
  display: flex;
  gap: 10px;
}

.btn {
  padding: 8px 16px;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  font-size: 1rem;
  transition: background-color 0.2s;
}

.btn.primary {
  background-color: #4CAF50;
  color: white;
}

.btn.primary:hover {
  background-color: #3e8e41;
}

.btn.secondary {
  background-color: #f1f1f1;
  color: #333;
}

.btn.secondary:hover {
  background-color: #e0e0e0;
}

.btn.danger {
  background-color: #f44336;
  color: white;
}

.btn.danger:hover {
  background-color: #d32f2f;
}

.btn.small {
  padding: 4px 8px;
  font-size: 0.9rem;
}

.btn:disabled {
  background-color: #cccccc;
  cursor: not-allowed;
}

.loading-indicator {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 30px 0;
}

.spinner {
  width: 40px;
  height: 40px;
  border: 4px solid #f3f3f3;
  border-top: 4px solid #4CAF50;
  border-radius: 50%;
  animation: spin 1s linear infinite;
  margin-bottom: 10px;
}

@keyframes spin {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}

.error-message {
  text-align: center;
  padding: 20px;
  color: #a94442;
}

.pagination {
  display: flex;
  justify-content: center;
  gap: 5px;
  margin-top: 20px;
}

.page-btn {
  width: 30px;
  height: 30px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid #ddd;
  border-radius: 4px;
  background-color: white;
  cursor: pointer;
}

.page-btn.active {
  background-color: #4CAF50;
  color: white;
  border-color: #4CAF50;
}

.user-detail-view {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
}

.user-detail-info {
  margin: 20px 0;
}

.user-detail-actions {
  display: flex;
  gap: 10px;
}

.explanation {
  margin-top: 40px;
}

.subsection {
  margin-bottom: 30px;
}

@media (max-width: 768px) {
  .dashboard {
    grid-template-columns: 1fr;
  }
}
</style>
